package org.hsqldb.index;

import org.hsqldb.lib.IntLookup;
import org.hsqldb.Row;
import org.hsqldb.RowBTree;
import org.hsqldb.persist.CachedObject;
import org.hsqldb.persist.PersistentStore;
import org.hsqldb.rowio.RowOutputInterface;

public class NodeBTreeBranch extends NodeBTree{

	protected int keyNumber;
	protected Object[] keys;
	protected NodeBTree[] childNodes;
	protected boolean isLeaf;
	protected NodeBTree nextLeaf;
	protected NodeBTree preLeaf;
	protected int[] position;
    
	protected NodeBTreeData nodeNextIndex;// node of next index (nNext==null || nNext.iId=iId+1)
	
    public NodeBTreeBranch(){
    	super(null, null);
    	
    	nodeParent = null;
    	positionInParent = NO_POS;
    	nodeNextIndex = null;
    	keys = new Object[TREE_NODE_LENGTH];
    	childNodes = new NodeBTree[TREE_NODE_LENGTH + 1];
    	nextLeaf = null;
    	preLeaf = null;
    	position = new int[TREE_NODE_LENGTH];
    	isLeaf = true;
    	keyNumber = 0;
    }
    
    public int getChildNumber(){
    	return keyNumber;
    }
    
	public NodeBTree getChild(PersistentStore store, int position) {
		if (position < 0 || position > TREE_NODE_LENGTH)
			return null;
    	return childNodes[position];
	}
	
	public NodeBTree getNextLeaf(PersistentStore store){
		if (!isLeaf)
			return null;
		return nextLeaf;
	}
	
	public void setNextLeaf(PersistentStore store,NodeBTree node){
		nextLeaf = node;
		setNodeChanged();
	}
	
	public NodeBTree getPreLeaf(PersistentStore store){
		if (!isLeaf)
			return null;
		return preLeaf;
	}
	
	public void setPreLeaf(PersistentStore store,NodeBTree node){
		preLeaf = node;
		setNodeChanged();
	}

    public NodeBTree getPrevSibling(PersistentStore store){
    	NodeBTreeBranch parentNode = (NodeBTreeBranch) getParent(store);
		if(parentNode == null)
			return null;
		if(positionInParent - 1 < 0 || positionInParent - 1 >= parentNode.getChildNumber())
			return null;
		return parentNode.getChild(store, positionInParent - 1);
    }
    
    
    public void setNodeChanged(){
    	
    }
	public NodeBTree getNextSibling(PersistentStore store) {
		NodeBTreeBranch parentNode = (NodeBTreeBranch) getParent(store);
		if(parentNode == null)
			return null;
		if(parentNode.getChildNumber() <= positionInParent + 1)
			return null;
		return parentNode.getChild(store, positionInParent + 1);
	}

	public boolean isLeaf(){
		return isLeaf;
	}
	public NodeBTreeBranch split(PersistentStore store,int pos){
		if (pos < 0 || pos >= keyNumber){
			return null;
		}
		NodeBTreeBranch newNode = new NodeBTreeBranch();
		setNodeChanged();
		newNode.setIsLeaf(isLeaf);
		for (int i = 0; i <= keyNumber - pos - 1; i++){
			if (isLeaf) {
				newNode.appendDataForLeaf(store,(Object[])keys[i+pos], (NodeBTreeData)getChild(store, i+pos));
			}
			else {
				newNode.appendKeyAndChildForNonLeaf(store,(Object[])keys[i+pos], (NodeBTreeBranch)getChild(store, i+pos));
			}
			keys[i+pos] = null;
			this.setChild(store,null, i + pos);//[i+pos] = null; 
		}
		// set next and pre leaf
		if(!isLeaf){
			//newNode.appendKeyAndChildForNonLeaf(store,null, (NodeBTreeBranch)childNodes[keyNumber]);
			newNode.setChild(store, this.getChild(store, keyNumber), newNode.getChildNumber());
		}
		keyNumber = pos;
		return newNode;
	}
	
	public boolean appendDataForLeaf(PersistentStore store,Object[] key, NodeBTreeData data){
		if(keyNumber >= TREE_NODE_LENGTH){
			return false;
		}
		if (!isLeaf)
			return false;
		setNodeChanged();
		keys[keyNumber] = key;
		this.setChild(store,data, keyNumber);//[childNumber] = data;
		NodeBTreeData x = data;
		while (x != null){
			x.setParent(store,this,keyNumber);
			x = (NodeBTreeData) x.getNext(store);
		}
		keyNumber++;
		return true;
	}
	
	public boolean appendKeyAndChildForNonLeaf(PersistentStore store,Object[] key, NodeBTreeBranch tree){
		if(keyNumber >= TREE_NODE_LENGTH){
			return false;
		}
		if (isLeaf)
			return false;
		setNodeChanged();
		if (key == null) {
			keys[keyNumber] = null;
			this.setChild(store,tree, keyNumber);// tree;
		}
		
		keys[keyNumber] = key;
		this.setChild(store,tree, keyNumber);
		tree.setParent(store, this, keyNumber);
		keyNumber++;
		
		return true;
	}
	
	public boolean insertIntoNonLeaf(PersistentStore store,Object[] key, NodeBTree tree, int pos){
		if(keyNumber >= TREE_NODE_LENGTH || pos > keyNumber || pos < 0){
			return false;
		}
		for (int i = keyNumber; i > pos; i--){
			keys[i] = keys[i-1];
			this.setChild(store, childNodes[i], i + 1);
			//NodeBTreeData x = (NodeBTreeData)this.getChild(store, i);//[i];
		}
		keyNumber++;
		keys[pos] = key;
		this.setChild(store, tree, pos + 1);
		((NodeBTreeBranch)tree).setParent(store,this,pos);
		setNodeChanged();
		return true;
	}
	
	
	public boolean insertDulplicateData(PersistentStore store,NodeBTreeData data, int pos){
		if (!isLeaf){
			return false;
		}
		
		if(keyNumber >= TREE_NODE_LENGTH || pos > keyNumber || pos < 0){
			return false;
		}
		setNodeChanged();
		if (getChild(store, pos) == null){
			setChild(store, data, pos);
		} else {
			NodeBTreeData node = (NodeBTreeData)getChild(store, pos);
			setChild(store, data, pos);
			data.setPre(store,null);
			data.setNext(store,node);
			node.setPre(store,data);
		}
		data.setParent(store, this, pos);
		return true;
	}
	
	public boolean insertDataAtLeaf(PersistentStore store,Object[] nodeKeys, NodeBTreeData data, int pos){
		if (!isLeaf){
			return false;
		}
		if(keyNumber >= TREE_NODE_LENGTH || pos > keyNumber || pos < 0){
			return false;
		}
		setNodeChanged();
		for(int i = keyNumber;i > pos;i--){
			keys[i] = keys[i-1];
			this.setChild(store, getChild(store, i - 1), i);
			//NodeBTreeData x = (NodeBTreeData)getChild(store, i);
		}
		keys[pos] = nodeKeys; 
		setChild(store, data, pos);
		data.setParent(store, this, pos);
		position[pos]=data.getPos();
		keyNumber++;
		return true;
	}
	
	@Override
	public boolean isInMemory(){
		return true;
	}
	public boolean isOver(){
		return (keyNumber >= TREE_NODE_LENGTH);
	}
	
	public void setChild(PersistentStore store,NodeBTree node,int position){
		
		childNodes[position] = node;
		setNodeChanged();
		if(node != null){
			if(isLeaf){
				while(node != null){
					node.setParent(store,this,position);
					node = ((NodeBTreeData)node).getNext(store);
				}
			}
			else{
				node.setParent(store,this,position);
			}
		}
	}
	
	public void setIsLeaf(boolean b){
		isLeaf = b;
		setNodeChanged();
	}
	@Override
    public void setParent(PersistentStore store,NodeBTree node,int pos){
    	nodeParent = node;
    	positionInParent = pos;
    	setNodeChanged();
    }
    
    public Object[] getKeys(int pos){
    	return (Object[]) keys[pos];
    }
    public void setKeys(Object[] values,int pos){
    	keys[pos] = values;
    	setNodeChanged();
    }

    
    
	public int getChildPos(int k) {
		// TODO Auto-generated method stub
		return position[k];
	}

	public void setChildPos(int pos, int k) {
		// TODO Auto-generated method stub
		position[k]=pos;
	}
	
}
